﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading;
using Arch.Aspects;
using System.Security;
using Arch.Common.IoC;
using Microsoft.Practices.Unity;

namespace Arch.Intercep.Interceptors
{
    public class SecurityInterceptor : IEntityInterceptor
    {
        [Dependency]
        public IDependencyContainer Container { get; set; }

        public object Intercept(object entity, ActionTarget target)
        {
            if (entity != null)
            {
                var securityAttribute = entity.GetType().GetCustomAttributes(true).Where(a => a is SecurityAccess).FirstOrDefault() as SecurityAccess;
                if (securityAttribute != null)
                {
                    if (ActionTarget.BeforeCud.HasFlag(target))
                    {
                        CheckSecurity(securityAttribute.WriteAccess, securityAttribute.WriteRoles);
                    }
                    else if (ActionTarget.Loading.HasFlag(target))
                    {
                        CheckSecurity(securityAttribute.ReadAccess, securityAttribute.ReadRoles);
                    }
                }
            }
            return entity;
        }

        protected virtual void CheckSecurity(Access access, string roles)
        {
            switch (access)
            {
                case Access.Authentified:
                    var principal = Container.GetInstance<IPrincipal>();
                    if (!principal.Identity.IsAuthenticated)
                    {
                        throw new SecurityException("User must be authenticated to perfom this operation");
                    }
                    if(!string.IsNullOrEmpty(roles))
                    {
                        var rolesArray = roles.Split(',').Select(r => r.Trim());

                        if (!rolesArray.Any(principal.IsInRole))
                        {
                            throw new SecurityException("User require one of these roles to perform the operation : " + roles);
                        }
                    }
                    break;
                case Access.None:
                    throw new SecurityException("Operation can't be performed for security reasons");
            }
        }
    }
}
